import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import load_boston

boston = load_boston()
#print(boston.data.shape)
X = boston.data
y = boston.target
f_names = boston.feature_names
df = pd.DataFrame(X, columns=f_names)
#print(df.head())
#print(df.info())
#print(df.columns)
#print(df.describe())

'''
题目一: 对连续型特征，可以用哪个函数可视化其分布？（给出你最常用的一个即可），并根据代码运行结果给出示例。

对连续型特征，我比较常用直方图可视化其分布，目前主要使用seaborn的distplot函数画直方图。
'''
def task1():
    fig = plt.figure()
    sns.distplot(df['RM'], bins=30, kde=True)
    plt.xlabel('RM value', fontsize=12)
    plt.show()

'''
题目二: 对两个连续型特征，可以用哪个函数得到这两个特征之间的相关性？根据代码运行结果，给出示例。

两个连续型特征，我们可以画出散点图来展示它们之间的相关性，使用seaborn的jointplot函数。
'''
def task2():
    sns.jointplot(x='AGE', y='DIS', data=df, kind='scatter')
    plt.show()


'''
题目三: 如果发现特征之间有较强的相关性，在选择线性回归模型时应该采取什么措施。

当输入特征之间存在共线性时，我们可以在线性回归模型中加入正则项。在这种情况下，我们一般采用L2正则。
'''


'''
题目四:当采用带正则的模型以及采用随机梯度下降优化算法时，需要对输入（连续型）特征进行去量纲预处理。课程代码给出了用标准化（StandardScaler）的结果，请改成最小最大缩放（MinMaxScaler）去量纲 ，
并重新训练最小二乘线性回归、岭回归、和Lasso模型。
'''
def task4():
    # 对连续型特征使用MinMaxScaler进行去量纲化
    X = boston.data
    y = boston.target
    from sklearn.preprocessing import MinMaxScaler
    mm_X = MinMaxScaler()
    mm_y = MinMaxScaler()
    X = mm_X.fit_transform(X)
    y = mm_y.fit_transform(y.reshape(-1, 1))
    fe_data = pd.DataFrame(data=X, columns=f_names, index=df.index)
    fe_data['MEDV'] = y
    print(fe_data.head())
    # 保存处理后数据供后面学习使用
    fe_data.to_csv('WF_boston_housing.csv', index=False)
def task4_2():
    from sklearn.linear_model import LinearRegression
    from sklearn.linear_model import RidgeCV
    from sklearn.linear_model import LassoCV
    from sklearn.model_selection import train_test_split
    from sklearn.metrics import r2_score
    # 读取数据并分离得到X和y
    data = pd.read_csv('WF_boston_housing.csv')
    y = data['MEDV']
    X = data.drop('MEDV', axis=1)
    f_names = X.columns
    # 随机采样20%数据构建测试样本，其余数据作为训练数据
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=30)

    # 训练最小二乘线性回归
    lr = LinearRegression()
    lr.fit(X_train, y_train)
    # 使用R2分数评价模型
    y_pred_train = lr.predict(X_train)
    lr_score_train = r2_score(y_train, y_pred_train)
    y_pred_test = lr.predict(X_test)
    lr_score_test = r2_score(y_test, y_pred_test)
    print('线性回归模型在训练集上的R2分数: {}'.format(lr_score_train))
    print('线性回归模型在测试集上的R2分数: {}'.format(lr_score_test))
    # 线性回归模型在训练集上的R2分数: 0.7430248871225487
    # 线性回归模型在测试集上的R2分数: 0.7181173900062092

    # 训练岭回归
    rg = RidgeCV()
    rg.fit(X_train, y_train)
    # 使用R2分数评价模型
    y_pred_train = rg.predict(X_train)
    rg_score_train = r2_score(y_train, y_pred_train)
    y_pred_test = rg.predict(X_test)
    rg_score_test = r2_score(y_test, y_pred_test)
    print('岭回归模型在训练集上的R2分数: {}'.format(rg_score_train))
    print('岭回归模型在测试集上的R2分数: {}'.format(rg_score_test))
    # 岭回归模型在训练集上的R2分数: 0.7429661628016178
    # 岭回归模型在测试集上的R2分数: 0.7196124206609074

    # 训练Lasso模型
    la = LassoCV()
    la.fit(X_train, y_train)
    # 使用R2分数评价模型
    y_pred_train = la.predict(X_train)
    la_score_train = r2_score(y_train, y_pred_train)
    y_pred_test = la.predict(X_test)
    la_score_test = r2_score(y_test, y_pred_test)
    print('Lasso回归模型在训练集上的R2分数: {}'.format(la_score_train))
    print('Lasso回归模型在测试集上的R2分数: {}'.format(la_score_test))
    # Lasso回归模型在训练集上的R2分数: 0.7427830702078159
    # Lasso回归模型在测试集上的R2分数: 0.7199115345891167

    # 画出各个模型中各个特征的权重比
    fs = pd.DataFrame({'columns': list(f_names), 'coef_lr': list(lr.coef_.T), 'coef_rg':list(rg.coef_.T), 'coef_lasso':list(la.coef_.T)})
    fs.sort_values(by=['coef_lr'], ascending=False)
    print(fs)

'''
题目五: 代码中给出了岭回归（RidgeCV）和Lasso（LassoCV）超参数（alpha_）调优的过程，请结合两个最佳模型以及最小二乘线性回归模型的结果，
给出什么场合应该用岭回归，什么场合用Lasso，什么场合用最小二乘。

结合题目四中得出的模型分数结果，最小二乘在训练集上的效果是最好的但是在测试集上的效果是最差的。而考虑我们的测试数据中特性之间是存在相关性的，
说明对于特性之间存在相关性的时候最下二乘效果不好，反之，我们可以说特性之间不存在相关性的时候可以考虑使用最小二乘。
比较岭回归和Lasso回归的结果，两种模型在训练集和测试集上的效果相差不多，岭回归相对好一点。而根据各个特征的权重比对比图，我们发现岭回归对特征的收缩更明显一点。
岭回归加入L2正则，Lasso加入L1正则，在超参数调优的过程中，我们都是采用循环递归取不同范围的值找到最佳值的。我们可以考虑岭回归求解的结论:
输入特性存在共线性，矩阵X是接近不满秩，我们对(XTX+λI)求逆可得到稳定解。因此，岭回归适用于输入特性存在共线性的情况。Lasso回归模型中我们可以
通过设置回归系数得到稀疏模型，因此，Lasso回归更适合于某些特征与目标变量之间存在强相关性的情况。
'''

if __name__ == '__main__':
    #task1()
    #task2()
    #task4()
    task4_2()
    os.system('pause')
